import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.sql.Time;
import java.util.Random;
import java.util.Timer;

public class CipherTextAttack {
	
	private String[] m_mostCommon = {"the","be","to","of","and","in","that","have","I","it","for","not","on","with","he","as","you","do","at","this","but","his","by","from"
			,"they","we","say","her","she","or","an","will","my","one","all","would","there","their","what","so","up","out","if","about"
			,"who","get","which","go","me","when","make","can","like","time","no","just","him","know","take","people","into","year","your","good"
			,"some","could","them","see","other","than","then","now","look","only","come","its","over","think","also","back","after","use","two","how"
			,"our","work","first","well","way","even","new","want","because","any","these","give","day","most","us"};
	private char[][] m_keyArray = new char[8][2];
	private int m_sizeOfInitialVector;
	private byte[][] m_blocks;
	private int m_tens;
	private byte[] m_initialVector;
	private byte[] m_encrypted;
	private byte[][][] m_decrypted;
	private int [] m_count = new int[8];
	private char[] m_order = {'e','a','h','d','f','b','c','g'};
	private byte[][][] m_tryThis;

	public CipherTextAttack(byte[] text, byte[] initialVector,int initialVectorSize)
			throws IOException {
		m_sizeOfInitialVector = initialVectorSize;
		m_tens = 0;
		if (text.length % 10 != 0)
			m_tens = 1;
		m_blocks = new byte[text.length / 10 + m_tens][10];// create room for
															// the text by blocks
		m_initialVector = initialVector;
		MoveToBlocks(text);// move to block of ten
		int i;
		for (i = 0; i < 8; i++) {// puts in the array
			m_keyArray[i][0] = (char) (((int) 'a') + i);
		}
	}

	public void Encrypt(String key) throws IOException {
		EncryptionKey(key);// generate a key
		int i, j;
		m_encrypted = new byte[10];
		for(i=0;i<m_sizeOfInitialVector;i++)
			m_encrypted[i] = m_initialVector[i];
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < m_blocks[i].length; j++) {
				m_encrypted[j] = (byte) (m_blocks[i][j] ^ m_encrypted[j]);
				// does the Xor between the initial vector and the text
				if(m_encrypted[j]>'a'-1 && m_encrypted[j] < 'h' + 1)
					ChangeToEncryptedKey(j);
			}
			FileWriter file = new FileWriter("file.txt", true);
			file.write(new String(m_encrypted));
			file.close();
		}
	}
	
	private void Compare(int length,int start,int place){
		int i,j;
		boolean[] difArray = new boolean[length];
		int dif = 3;
		for(i = 0; i < m_mostCommon.length;i++){
			if(length == m_mostCommon[i].length()){
				dif = 0;
				for(j = 0; j < length && dif < 3;j++){
					if(m_tryThis[place][start+j][0] != m_mostCommon[i].charAt(j)){
						dif++;
						if(m_decrypted[place][start+j][1] != -1)
							difArray[j] = true;
					}
				}
			}
		}
	}
	private void CompareTwo(int length,int start,int place,int lines){
		if (lines < 2) {
			int i, j;
			boolean[] difArray = new boolean[length];
			// if(lenght == 1)
			int dif = 3;
			for (i = 0; i < m_mostCommon.length; i++) {
				if (length == m_mostCommon[i].length()) {
					dif = 0;
					for (j = 0; j < length && dif < 2; j++) {
						if (start + j < 10){
							if (m_tryThis[place - lines][start + j][0] != m_mostCommon[i]
									.charAt(j)) {
								dif++;
								difArray[j] = true;
							}
						}else{
							if (m_tryThis[place][10 - start + j][0] != m_mostCommon[i]
									.charAt(j)) {
								dif++;
								difArray[j] = true;
							}
						}
					}
				}
			}
		}
	}
	
	
	private void ChangeToEncryptedKey(int i){
			m_encrypted[i] = (byte) m_keyArray[ m_encrypted[i] - 'a'][1]; 
			}
	
	public void Decrypt() throws IOException{
		int i,j;
		m_encrypted = new byte[10];
		m_decrypted = new byte[m_blocks.length][10][2];
		//m_tryThis = new byte[m_blocks.length][10][2];
		for(i = 0; i < m_blocks.length; i++){
			for(j = 0; j < 10; j++){
				m_encrypted[j] = m_blocks[i][j];
			}
			for(j = 0; j < 10;j++){
				if(m_encrypted[j] >= 'a' && m_encrypted[j] <= 'h'){
					m_decrypted[i][j][1] = m_initialVector[j];
					m_decrypted[i][j][0] = m_encrypted[j];
					m_count[m_encrypted[j] - 'a']++;
				}else{
					m_decrypted[i][j][1] = -1;
					m_decrypted[i][j][0] =  (byte)(m_initialVector[j] ^ m_encrypted[j]);
				//	m_tryThis[i][j][0] = (byte)(m_initialVector[j] ^ m_encrypted[j]);
				}
			}
			m_initialVector = m_blocks[i];
		}
		TryThis();
		
	//	DecryptKey();
		return;
	}
	
	private void TryThis(){
		int i,j,k;
	//	int  g=0,f=0;
		char[] check=new char[8];
		int[] count=new int[8];
	//	int[][] fakeKey=new int[8][8];
	//	int[][] fakeKey1=new int[8][8];
		int[][] key = new int[8][8];
			for(i = 0; i < m_decrypted.length;i++){
				for(j = 0; j < 10; j++){
					if(m_decrypted[i][j][0] >= 'a' && m_decrypted[i][j][0] <= 'h' && m_decrypted[i][j][1] != -1){
						for(k = 0; k < 8 ; k++){
							if(key[m_decrypted[i][j][0] - 'a'][k] < 0){
							}
							else
							{
								int temp=((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) ;
							if((temp > 31 && temp < 127) ||(temp > 8  && temp < 11) ||temp==13||temp==0  )
							{
							
							}
							else{
								key[m_decrypted[i][j][0] - 'a'][k] = Integer.MIN_VALUE;
							}
							}
						/*	if((((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) > 31 && ((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) < 127) ||(((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) > 10  && ((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) < 15))
							{
							
							}
							else{
								fakeKey[m_decrypted[i][j][0] - 'a'][k] = Integer.MIN_VALUE;
							}
							if((((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) > 32 && ((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) < 127) ||(((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) > 9  && ((byte)(k +'a') ^ Math.abs(m_decrypted[i][j][1])) < 15 ))
							{
							
							}
							else{
								fakeKey1[m_decrypted[i][j][0] - 'a'][k] = Integer.MIN_VALUE;
							}
						}*/
					}	
				}
			}
			}
		for (i = 0; i < 8; i++){
			for(j = 0; j < 8;j++){
				
				if(key[i][j] > -1)/* && fakeKey[i][j]<-1&&fakeKey1[i][j]<-1)*/{
					count[j]++;
					if(count[j]>1)
						check[j]=m_keyArray[j][1];
					m_keyArray[j][1] = (char) ('a' + i);
					System.out.print((char)('a' + j) +":" +" " + (char)('a' + i)+" ");
					//m_keyArray[j][1] = (char) ('a' + i);
				}
				
			}
			System.out.println();
		}
//				if(count[i]==1){
//				
//					for(j=0;j<8;j++)
//					{
//						if(key[i][j] > -1){
//							m_keyArray[j][1] = (char) ('a' + i);}
//				
//					}
//				}
		//	}
		for(i=0;i<8;i++)
		{
			if(count[i]>1)
			{
				for(j=0;j<8;j++){
				//	if(count[j]==1)
					//	continue;
					if(m_keyArray[i][1]==m_keyArray[j][1]&&i!=j&&count[j]==1)
						m_keyArray[i][1]=check[i];
				}
			}
				
		}
//		System.out.println("------------------------");
//		for(i=0;i<8;i++)
//			System.out.println((char)('a' + i) +":" +" " + m_keyArray[i][1]+" ");
//		
	}

	private void DecryptKey() throws IOException {
		int i, j;
		int max = 0;
		int place = -1;
		int[] temp = m_count.clone();
		for (j = 0; j < temp.length; j++) {
			for (i = 0; i < temp.length; i++) {
				if (temp[i] > max) {
					place = i;
					max = m_count[i];
				}
			}
			max = 0;
			m_keyArray[m_order[j] - 'a'][1] = (char)(place + 'a');
			temp[place] = 0;
			place = 0;
		}
		DecryptFile();
	}
	
	private boolean CheckDecrypt(int i,int j,int place){
		if(place < j){
		for(;place < j;place++){
			if(m_decrypted[i][place][1] != -1)
				return true;
		}
		}else
		{
		for(;place < 10; place++)
			if(m_decrypted[i-1][place][1] != -1)
				return true;
		for(place = 0;place < j;place++){
			if(m_decrypted[i][place][1] != -1)
				return true;
		}
		
		}
		return false;
	}
	
	private void DecryptFile() throws IOException{
		int i,j;
		int place = -1;
		int index = 0;
		byte[] temp = new byte[10];
		for(i = 0 ;i < m_tryThis.length; i++){
			for(j = 0; j < 10;j++){
				if(m_decrypted[i][j][1] == -1 && m_decrypted[i][j][0] == ' '){
					if(place != -1)
						if(CheckDecrypt(i, j, place))
							if(j > place)
								Compare(j-place-1,place,i);
							if(place > j)
								CompareTwo(10-place+j,place,i,i-index);
					place = j + 1;
					index = i;
					
				}
				if(m_tryThis[i][j][1] != -1 && m_tryThis[i][j][1] != 0){
					m_tryThis[i][j][0] = (byte) m_keyArray[m_decrypted[i][j][0] - 'a'][1];
					m_tryThis[i][j][0] = (byte) (m_decrypted[i][j][1] ^ m_decrypted[i][j][0]);
					m_tryThis[i][j][1] = -1;
				}
				temp[j] = m_tryThis[i][j][0];
			}
			FileWriter file = new FileWriter("file3.txt", true);
			file.write(new String(temp));
			file.close();
		}
	}
	
	
	private void EncryptionKey(String key) {
		int i;
		//Random random = new Random();
		Boolean check=false;
		char pos=' ';
		for (i = 0; i <key.length(); i++) {
				if(key.charAt(i)==' ' || key.charAt(i)=='\n')
					continue;
				else
				{
					if(check==false)
					{
						pos=key.charAt(i);
						check =true;
					}
					else
					{
						check=false;
						m_keyArray[pos-'a'][1] =key.charAt(i);
					}
				}
			//do {
				//create = random.nextInt(8);// draws a number
		//	} while (NotFoundTwoDimensionArray(i, create));
			//check if we used the letter or it is the same letter
				// found new letter to put in the
															// encryption key
		}
	}

	private void MoveToBlocks(byte[] text) {
		int i, j;
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < 10; j++) {
				if (text.length > i * 10 + j)// check if length of the array is
												// bigger or it needs to put
												// zero
					m_blocks[i][j] = text[i * 10 + j];//puts the value inside of it
				else
					m_blocks[i][j] = 0;//if bigger puts zero
			}
		}
	}

	private boolean NotFoundTwoDimensionArray(int index, int random) {
		if (index == 0)// if first it is ok for sure
			return false;
		int i;
		for (i = 0; i < index; i++) {// check if in the array we used the letter
										// already
			if (m_keyArray[i][1] == (char) (random + (int) 'a'))
				return true;
		}
		return false;
	}

	public char[] GetKey() {
		int i;
		char[] res = new char[8];
		for (i = 0; i < 8; i++)
			res[i] = m_keyArray[i][1];
		return res;
	}

	public static void main(String[] args) throws IOException {
		int i; 
		byte[] key;
		String start = args[0];
		
		if (start.equals("EncryptionA"))
		{
			File first = new File(args[1]);
			File second = new File(args[3]);
			File third =new File(args[2]);
			key=Files.readAllBytes(third.toPath());
			String Skey=new String(key);
			int initialVectorSize = 10;
			byte[] text = Files.readAllBytes(first.toPath());// reads the file to bytes
			byte[] initialVector = Files.readAllBytes(second.toPath());
			CipherTextAttack toEncrypt = new CipherTextAttack(text,initialVector,initialVectorSize);
				toEncrypt.Encrypt(Skey);
				char[] res = toEncrypt.GetKey();
				FileWriter writer = new FileWriter("myKey.txt");
				for(i = 0; i < res.length;i++){
					char temp = 'a';
					temp = (char) (temp + i);
					writer.write(temp+ " " + res[i]+'\n');		}
				writer.close();
			}
			 
			 
		
		if (start.equals("DecryptionA")){
			File first = new File(args[1]);
			File second = new File(args[2]);
			int initialVectorSize = 10;
			byte[] text = Files.readAllBytes(first.toPath());// reads the file to bytes
			byte[] initialVector = Files.readAllBytes(second.toPath());
			CipherTextAttack toEncrypt = new CipherTextAttack(text,initialVector,initialVectorSize);
				toEncrypt.Decrypt();
				char[] res = toEncrypt.GetKey();
				FileWriter writer = new FileWriter("Key.txt");
				for(i = 0; i < res.length;i++){
					char temp = 'a';
					temp = (char) (temp + i);
					writer.write(res[i]+ " " + temp+'\n');
				}
				writer.close();
			 
		
			
		}	
		
		
	/*	int initialVectorSize = 10;
		byte[] text = Files.readAllBytes(first.toPath());// reads the file to bytes
		byte[] initialVector = Files.readAllBytes(second.toPath());
		CipherTextAttack toEncrypt = new CipherTextAttack(text,
				initialVector,initialVectorSize);
		if (start.equals("DecryptionA")) {
			toEncrypt.Decrypt();
			char[] res = toEncrypt.GetKey();
			FileWriter writer = new FileWriter("myKey.txt");
			for(i = 0; i < res.length;i++){
				char temp = 'a';
				temp = (char) (temp + i);
				writer.write(temp+ " " + res[i]+'\n');
			}
			writer.close();
		}
		if(start.equals("EncryptionA")){
			toEncrypt.Encrypt();
			char[] res = toEncrypt.GetKey();
			FileWriter writer = new FileWriter("myKey.txt");
			for(i = 0; i < res.length;i++){
				char temp = 'a';
				temp = (char) (temp + i);
				writer.write(temp+ " " + res[i]+'\n');		}
			writer.close();
		}*/
	}

	public void PrintArray(char[][] array) {
		int i;
		for (i = 0; i < array.length; i++) {
			System.out.println(array[i][0]);
		}
	}
}
